﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.Utils;

namespace Algoritmia.DataStructures.Lists
{
    [Serializable]
    public class ArrayList<T> : IList<T>
    {
        private T[] _array;
        private int _count;
        private readonly EqualityComparison<T> _equalityComparison;

        public ArrayList(IEnumerable<T> items=null, EqualityComparison<T> equalityComparison = null)
        {
            items = items ?? Enumerable.Empty<T>();
            _equalityComparison = AlgoritmiaFactory.GetDefaultEqualityComparison(equalityComparison);
            var tmp = items.ToArray();
            _array = tmp.Length != 0 ? tmp : new T[1];
            _count = tmp.Length;
        }

        #region IList<T> Members

        public virtual void Add(T elem)
        {
            if (_count == _array.Length)
            {
                DoubleCapacity();
            }
            _array[_count++] = elem;
        }

        public virtual void Clear()
        {
            _count = 0;
            _array = new T[1];
        }

        public bool Contains(T item)
        {
            for(int i = 0; i < _count; i++)
            {
                if ( _equalityComparison(_array[i],item))
                {
                    return true;
                }
            }
            return false;
        }

        public virtual T this[int index]
        {
            get
            {
                if (index < 0 || index >= _count)
                {
                    throw new IndexOutOfRangeException();
                }
                return _array[index];
            }
            set
            {
                if (index < 0 || index >= _count)
                {
                    throw new IndexOutOfRangeException();
                }
                _array[index] = value;
            }
        }


        public int IndexOf(T elem)
        {
            for (int i = 0; i < _count; i++ )
            {
                if ( _equalityComparison(elem,_array[i]))
                {
                    return i;
                }
            }
            return -1;
        }

        public virtual void InsertAt(T elem, int index)
        {
            if (index < 0 || index > _count)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (_count == _array.Length)
            {
                DoubleCapacity();
            }
            for (var i = _count; i > index; i--)
            {
                _array[i] = _array[i - 1];
            }
            _count++;
            _array[index] = elem;
        }



        public bool Remove(T item)
        {
            for(var i = 0 ; i < _count; i++)
            {
                if (_equalityComparison(item,_array[i]))
                {
                    _count--;
                    for (var k = i; k < _count; k++)
                    {
                        _array[k] = _array[k + 1];
                    }
                     if (_count < _array.Length / 2)
                     {
                         HalveCapacity();
                     }
                    return true;
                }
            }
            return false;
        }



        public virtual void RemoveAt(int index)
        {
            if (index < 0 || index >= _count)
                throw new ArgumentOutOfRangeException();
            _count--;
            for (int i = index; i < _count; i++)
                _array[i] = _array[i + 1];

            if (_count < _array.Length / 2)
                Array.Resize(ref _array, _array.Length / 2);
        }

        #endregion

        #region Helper Methods

        private void HalveCapacity()
        {
            Array.Resize(ref _array, _array.Length / 2);
        }

        private void DoubleCapacity()
        {
            Array.Resize(ref _array, _array.Length * 2);
        }

        #endregion

        #region ICountable Members

        public int Count
        {
            get { return _count; }
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < _count; i++)
                yield return _array[i];
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        public override string ToString()
        {
            return String.Format("{0} <{1}> [{2}]", GetType().Name, typeof(T).Name, string.Join(", ", this));
        }
    }
}
